/*
 * Android Runner is a multiplayer GPS game fully written by Xurxo Mendez Perez
 * 
 * Copyright (C) 2009 Xurxo Mendez Perez
 *   
 * This file is part of Android Runner.
 * 
 * Android Runner is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Android Runner is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Android Runner.  If not, see <http://www.gnu.org/licenses/>.
 */

package es.sonxurxo.gpsgame.client.cv.activity;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NotificationManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteException;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;

import es.sonxurxo.gpsgame.client.R;
import es.sonxurxo.gpsgame.client.cv.common.actions.CommonActions;
import es.sonxurxo.gpsgame.client.cv.common.dialogs.CommonDialogs;
import es.sonxurxo.gpsgame.client.cv.service.WaitUntilGameStartService;
import es.sonxurxo.gpsgame.client.cv.util.constants.CustomResultCodes;
import es.sonxurxo.gpsgame.client.cv.util.location.LocationToGeoPointConversor;
import es.sonxurxo.gpsgame.client.model.service.game.HttpGameServiceImpl;
import es.sonxurxo.gpsgame.client.model.service.to.Game;
import es.sonxurxo.gpsgame.client.model.service.to.GameCTO;
import es.sonxurxo.gpsgame.client.model.service.to.Player;
import es.sonxurxo.gpsgame.client.util.DateOperations;
import es.sonxurxo.gpsgame.client.util.db.PendingGamesDbAdapter;
import es.sonxurxo.gpsgame.client.util.exception.ServerException;

/**
 * 
 * @author "Xurxo Mendez Perez"
 *
 */
public class ViewGamesActivity extends Activity implements Abandonable {

	private static final int LOGOUT_ID = Menu.FIRST;
	private static final int JOIN_GAME_ID = Menu.FIRST + 1;

    private static final int OLD_GAME_DIALOG_ID = CommonDialogs.FIRST_CUSTOM_DIALOG_ID;
    
    public static final int JOINED_RESULT_CODE = 0;

    public static final int METHOD_CITY = 0;
    public static final int METHOD_DISTANCE = 1;
    public static final int ELEMENTS_PER_PAGE = 2;
    
	private Game[] gameArray;
	private int partialGameIndex = 0;
	private long currentGameId;
	
	private Player player;
	private Location currentLocation;
	
	private boolean joinedToThisGame = false;
	
	public static int startIndex;
	public static int count;
	
	private static boolean hasMore;
	private static int absolutGameIndex = 0;
	
    private TextView gameNumberView, joinedView, activeView, 
    	gameIdView, gameCityView, gameStartDateView, 
    	gameFinishDateView, gamePointsToWinView, gameAvailablePointsView, gameCurrentPlayersView,
    	gameMaxPlayersView;
    private Button nextButton, previousButton;

    private String city = null;
    private int distance = 0;
    
    private FindGamesTask findGamesTask;
    private JoinGameTask joinGameTask;
    private DbCreateTask dbCreateTask;
    
    private class NextOnClickListener implements OnClickListener {

    	NextOnClickListener() {}
		public void onClick(View v) {

			Animation animation = AnimationUtils.loadAnimation(
    				ViewGamesActivity.this, R.anim.slide_left);
    		
    		if (partialGameIndex + 1 >= count) {
    			startIndex = startIndex + count;
    			if (city != null) {
    				launchFindGamesThread(city);
    			}
    			else if (distance != 0) {
    				launchFindGamesThread(distance);
    			}
    			absolutGameIndex++;
    			return;
    		}
    		absolutGameIndex++;
            View v2 = findViewById(R.id.vg_tv_gameId);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_active);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamecity);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamecurrentplayers);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamefinishdate);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamestartdate);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamemaxplayers);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamepointstowin);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_availablepoints);
            v2.startAnimation(animation);
            fillGames();
		}
    }

    private class PreviousOnClickListener implements OnClickListener {

    	PreviousOnClickListener() {}
		public void onClick(View v) {
			

			Animation animation = AnimationUtils.loadAnimation(
    				ViewGamesActivity.this, R.anim.slide_right);
    		
    		if (absolutGameIndex == startIndex) {
    			startIndex = startIndex - count;
    			if (city != null) {
    				launchFindGamesThread(city);
    			}
    			else if (distance != 0) {
    				launchFindGamesThread(distance);
    			}
    			absolutGameIndex--;
    			return;
    		}
    		absolutGameIndex--;
            View v2 = findViewById(R.id.vg_tv_gameId);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_active);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamecity);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamecurrentplayers);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamefinishdate);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamestartdate);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamemaxplayers);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_gamepointstowin);
            v2.startAnimation(animation);
            v2 = findViewById(R.id.vg_tv_availablepoints);
            v2.startAnimation(animation);
            fillGames();
		}
    }

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
        setContentView(R.layout.view_games_page);
		getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_1);
		
		gameNumberView = (TextView)findViewById(R.id.vg_tv_gameNumber);
		joinedView = (TextView)findViewById(R.id.vg_tv_joined);
		activeView = (TextView)findViewById(R.id.vg_active);
		gameIdView = (TextView)findViewById(R.id.vg_tv_gameId);
        gameCityView = (TextView)findViewById(R.id.vg_tv_gamecity);
        gameStartDateView = (TextView)findViewById(R.id.vg_tv_gamestartdate);
        gameFinishDateView = (TextView)findViewById(R.id.vg_tv_gamefinishdate);
        gamePointsToWinView = (TextView)findViewById(R.id.vg_tv_gamepointstowin);
        gameAvailablePointsView = (TextView)findViewById(R.id.vg_tv_availablepoints);
        gameCurrentPlayersView = (TextView)findViewById(R.id.vg_tv_gamecurrentplayers);
        gameMaxPlayersView = (TextView)findViewById(R.id.vg_tv_gamemaxplayers);
        nextButton = (Button)findViewById(R.id.vg_b_next);
        previousButton = (Button)findViewById(R.id.vg_b_previous);
        
        player = (Player)getIntent().getExtras().getSerializable("player");
        
        ((TextView)findViewById(R.id.title_right_text)).setText(player.getLogin());
		
        startIndex = 0;
        absolutGameIndex = 0;
        count = ELEMENTS_PER_PAGE;
        
        try {
        	dismissDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
        } catch (Exception e) {

        }
        try {
        	dismissDialog(CommonDialogs.LOADING_DIALOG_ID);
        } catch (Exception e) {

        }        

		city = getIntent().getExtras().getString("city");
		
		if (city != null) {
			launchFindGamesThread(city);
		}
		else {
			
			currentLocation = 
				(Location)getIntent().getExtras().getParcelable("location");
			distance = Integer.parseInt(
					getIntent().getExtras().getString("distance"));
			launchFindGamesThread(distance);
		}
	}
	
	@Override
	public void onResume() {
		super.onResume();
	}
	
	private void showToast(int messageId) {
		Toast.makeText(this, messageId,
                Toast.LENGTH_SHORT).show();
	}
	
	private void doViewGames() {

		if (gameArray.length > 0)
			fillGames();
		else {
			Intent mIntent = new Intent();
			Bundle bundle = new Bundle();
			bundle.putSerializable("player", player);
			mIntent.putExtras(bundle);
			setResult(CustomResultCodes.VG_NO_GAMES_RESULT_CODE, mIntent);
			showDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
			finish();
		}
	}
	
	private void launchFindGamesThread(String city) {
		findGamesTask = new FindGamesTask(city);
		Thread findGamesThread = new Thread(null, findGamesTask, "Login");
		findGamesThread.start();
		showDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);		
	}
	
	private void launchFindGamesThread(int distance) {
		distance = distance * 10000;
		GeoPoint currentGP = LocationToGeoPointConversor.getGeoPoint(currentLocation);
		findGamesTask = new FindGamesTask(distance,
				currentGP.getLatitudeE6(), currentGP.getLongitudeE6());
		Thread findGamesThread = new Thread(null, findGamesTask, "Login");
		findGamesThread.start();
		showDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
	}
	
	private void setHeader(long gameId) {
		String s = new String("Game " + gameId);
		gameNumberView.setText(s);
	}

	
	private void fillGames() {
		partialGameIndex = absolutGameIndex - startIndex;

		Game game = gameArray[partialGameIndex];
		currentGameId = game.getGameId();

		setHeader(currentGameId);
		
		if (hasMore) {
				nextButton.setVisibility(View.VISIBLE);
				nextButton.setOnClickListener(new 
						NextOnClickListener());
		}
		else {
			nextButton.setVisibility(View.INVISIBLE);
		}
		
		if (absolutGameIndex > 0) {
			previousButton.setVisibility(View.VISIBLE);
			previousButton.setOnClickListener(new 
					PreviousOnClickListener());
		}
		else {
			previousButton.setVisibility(View.INVISIBLE);
		}
		gameIdView.setText(String.valueOf(currentGameId));
		if (game.getStartDate().getTimeInMillis() <= System.currentTimeMillis()) {
			activeView.setVisibility(View.VISIBLE);
			activeView.setText("ACTIVE");
			Animation animation = AnimationUtils.loadAnimation(this, R.anim.hyperspace_in_slow);
			animation.setRepeatCount(Animation.INFINITE);
			activeView.setAnimation(animation);
			activeView.startAnimation(animation);
			
		}
		else {
			activeView.clearAnimation();
			activeView.setVisibility(View.INVISIBLE);
		}
		gameCityView.setText(game.getCity());
		gameStartDateView.setText(DateOperations.formatDate(game.getStartDate()));
    	gameFinishDateView.setText(DateOperations.formatDate(game.getFinishDate()));
    	gamePointsToWinView.setText(String.valueOf(game.getPointsToWin()));
    	gameAvailablePointsView.setText(String.valueOf(game.getAvailablePoints()));
    	gameCurrentPlayersView.setText(String.valueOf(game.getCurrentPlayers()));
	    gameMaxPlayersView.setText(" / " + String.valueOf(game.getMaxPlayers()));
	    if (game.getCurrentPlayers() 
    			== game.getMaxPlayers()) {
    		gameMaxPlayersView.append(" (COMPLETE)");
    	}

    	
    	if (player.getGameId() == currentGameId) {
    		joinedToThisGame = true;
    		joinedView.setVisibility(View.VISIBLE);
    		joinedView.setText(R.string.vg_you_are_here);
    	}
    	else {
    		joinedView.setVisibility(View.INVISIBLE);
    		joinedToThisGame = false;
    	}
	}
	
	private void doLogout() {
		CommonActions.launchLogoutThread(player.getLogin(), this);
	}

	private void preJoinGame(long gameId) {
		long oldGameId = 
    		player.getGameId();
    	if (oldGameId != -1) {
    		showDialog(OLD_GAME_DIALOG_ID);
    		return;
    	}
    	else
    		launchJoinGameThread(gameId);
	}
	
	private void continueJoinGame(Game game) {
		Intent mIntent = new Intent();
		Bundle bundle = new Bundle();
		player.setGameId(game.getGameId());
		bundle.putSerializable("player", player);
		bundle.putSerializable("game", game);
		mIntent.putExtras(bundle);
    	setResult(RESULT_OK, mIntent);
    	showDialog(CommonDialogs.LOADING_DIALOG_ID);
		finish();
	}
	
	private void doJoinGame(long gameId) {
		Game game = gameArray[partialGameIndex];
		launchDbCreateThread(game);
	}
	
	private void launchDbCreateThread(Game game) {
		dbCreateTask = new DbCreateTask(game);
		Thread dbCreateThread = new Thread(null, dbCreateTask, "DbCreateGame");
		dbCreateThread.start();
		showDialog(CommonDialogs.LOADING_DIALOG_ID);
	}
	
	private void launchJoinGameThread(long gameId) {
		joinGameTask = new JoinGameTask(gameId);
		Thread joinGameThread = new Thread(null, joinGameTask, "JoinGame");
		joinGameThread.start();
		showDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
	}
	
    @Override
    protected Dialog onCreateDialog(int id) {
    	Dialog d = CommonDialogs.createDialog(id, this);
		if (d != null)
			return d;
    	switch(id) {
    	case OLD_GAME_DIALOG_ID:
        	return new AlertDialog.Builder(this)
            .setIcon(R.drawable.alert_dialog_icon)
            .setTitle(R.string.info)
            .setMessage(R.string.vg_old_game)
            .setPositiveButton(R.string.join_anyway, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                	doRemoveFromGame();
                }
            })
            .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int whichButton) {
                	try {
                		dismissDialog(OLD_GAME_DIALOG_ID);
                	} catch (IllegalArgumentException e) {
                		
                	}
                }
            })
           .create();
    	}
        return null;
    }
    
    
    
    public void doAfterAbandon() {
    	launchJoinGameThread(currentGameId);
	}

	private void doRemoveFromGame() {
		CommonActions.launchAbandonGameThread(
				player, gameArray[partialGameIndex], this);
		joinedView.setVisibility(View.INVISIBLE);
		joinedToThisGame = false;
		((NotificationManager)getSystemService(NOTIFICATION_SERVICE)).
		cancel(R.string.cancel);
		stopService(new Intent(this, WaitUntilGameStartService.class));
    }

	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        menu.add(0, LOGOUT_ID,0, R.string.vg_mo_logout)
        	.setIcon(R.drawable.logout);
        menu.add(0, JOIN_GAME_ID,0, R.string.vg_mo_join)
        	.setIcon(R.drawable.join);
        return true;
    }
    
    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        switch(item.getItemId()) {
        case LOGOUT_ID:
        	doLogout();
        	break;
        case JOIN_GAME_ID:
        	if (!joinedToThisGame)
        		preJoinGame(gameArray[partialGameIndex].getGameId());
        	else
        		showToast(R.string.vg_this_game);
        	break;
        }
        
        return super.onMenuItemSelected(featureId, item);
    }
    
	private class FindGamesHandler extends Handler {

		public FindGamesHandler(Looper looper) {
			super(looper);
		}
		
		@Override
		public void handleMessage(Message msg) {
			try {
				dismissDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
			} catch (IllegalArgumentException e) {
				
			}
			ServerException sE = 
				(ServerException)msg.getData().getSerializable("ServerException");
			if (sE	!= null) {
				CommonDialogs.errorMessage = sE.getMessage();
	        	showDialog(CommonDialogs.SERVER_ERROR_DIALOG_ID);
	        	return;
	        }
        	Exception e = 
	        	(Exception)msg.getData().getSerializable("Exception");
	        	if (e != null) {
	        		CommonDialogs.errorMessage = e.getMessage();
		        	showDialog(CommonDialogs.CLIENT_ERROR_DIALOG_ID);
		        	return;
	        	}
			Game [] gameArray2 = 
				(Game [])msg.getData().getSerializable("gameArray");
			if (gameArray2 != null) {
				hasMore = msg.getData().getBoolean("hasMore");
				gameArray = gameArray2;
				doViewGames();
			}
		}
	}
	
	private class FindGamesTask implements Runnable {

		String city = null;
		int distance, latitude, longitude;
		HttpGameServiceImpl gameService;
		
		FindGamesTask(String city) {
			this.city = city;
			gameService = new HttpGameServiceImpl();
		}
		
		FindGamesTask(int distance, int latitude, int longitude) {
			this.distance = distance;
			this.latitude = latitude;
			this.longitude = longitude;
			gameService = new HttpGameServiceImpl();
		}
		
		public void run() {
			
			FindGamesHandler handler = new FindGamesHandler(Looper.getMainLooper());
			Bundle data = new Bundle();
			Message msg = new Message();
			try {
				GameCTO gameCTO;
				if (city != null) 
					gameCTO = gameService.findGamesByCity(city, 
							ViewGamesActivity.startIndex, ViewGamesActivity.count);
				else
					gameCTO = gameService.findGamesByLocation(
							latitude, longitude, distance, ViewGamesActivity.startIndex,
							ViewGamesActivity.count);
				data.putSerializable("gameArray", gameCTO.getGameList().
						toArray(new Game [0]));
				data.putBoolean("hasMore", gameCTO.isHasMore());
				msg.setData(data);
				handler.sendMessage(msg);

			} catch (ServerException e) {
	        	data.putSerializable("ServerException", e);
	        	msg.setData(data);
				handler.sendMessage(msg);
	        } catch (Exception e) {
	        	data.putSerializable("Exception", e);
	        	msg.setData(data);
				handler.sendMessage(msg);
	        }
		}
	}

	private class JoinGameHandler extends Handler {

		public JoinGameHandler(Looper looper) {
			super(looper);
		}
		
		@Override
		public void handleMessage(Message msg) {
			try {
				dismissDialog(CommonDialogs.CONNECTING_TO_SERVER_DIALOG_ID);
			} catch (IllegalArgumentException e) {
				
			}
			ServerException sE = 
				(ServerException)msg.getData().getSerializable("ServerException");
			if (sE	!= null) {
				CommonDialogs.errorMessage = sE.getLocalizedMessage();
				if (sE.getCode() == ServerException.INSTANCE_NOT_FOUND_CODE)
					showDialog(CommonDialogs.CONNECTION_LOST_DIALOG_ID);
				else
					showDialog(CommonDialogs.SERVER_ERROR_DIALOG_ID);
	        	return;
	        }
        	Exception e = 
	        	(Exception)msg.getData().getSerializable("Exception");
	        	if (e != null) {
	        		CommonDialogs.errorMessage = e.getLocalizedMessage();
		        	showDialog(CommonDialogs.CLIENT_ERROR_DIALOG_ID);
		        	return;
	        	}
			if (msg.getData().getBoolean("result")) {
				long gameId = msg.getData().getLong("gameId");
				doJoinGame(gameId);
			}
		}
	}
	
	private class JoinGameTask implements Runnable {

		long gameId;
		HttpGameServiceImpl gameService;
		
		JoinGameTask(long gameId) {
			this.gameId = gameId;
			gameService = new HttpGameServiceImpl();
		}
		
		public void run() {
			
			JoinGameHandler handler = new JoinGameHandler(Looper.getMainLooper());
			Bundle data = new Bundle();
			Message msg = new Message();
			try {
				boolean result = 
					gameService.joinGame(gameId);
				data.putBoolean("result", result);
				data.putLong("gameId", gameId);
				msg.setData(data);
				handler.sendMessage(msg);
				
			} catch (ServerException e) {
	        	data.putSerializable("ServerException", e);
	        	msg.setData(data);
				handler.sendMessage(msg);
	        } catch (Exception e) {
	        	data.putSerializable("Exception", e);
	        	msg.setData(data);
				handler.sendMessage(msg);
	        }
		}
	}

	private class DbCreateHandler extends Handler {

		public DbCreateHandler(Looper looper) {
			super(looper);
		}
		
		@Override
		public void handleMessage(Message msg) {
			try {
				dismissDialog(CommonDialogs.LOADING_DIALOG_ID);
			} catch (IllegalArgumentException e) {
				
			}
			SQLiteException e = 
	        	(SQLiteException)msg.getData().getSerializable("SQLiteException");
	        	if (e != null) {
	        		CommonDialogs.errorMessage = e.getLocalizedMessage();
		        	showDialog(CommonDialogs.SQLITE_ERROR_DIALOG_ID);
		        	return;
	        	}
			if (msg.getData().getBoolean("resultOk")) {
				Game game = (Game)msg.getData().getSerializable("game");
				if (game != null)
					continueJoinGame(game);
			}
		}
	}
	
	private class DbCreateTask implements Runnable {

		Game game;
		private PendingGamesDbAdapter p = new PendingGamesDbAdapter(ViewGamesActivity.this);
		
		DbCreateTask(Game game) {
			this.game = game;
		}
		
		public void run() {
			
			DbCreateHandler handler = new DbCreateHandler(Looper.getMainLooper());
			Bundle data = new Bundle();
			Message msg = new Message();
			try {
	        	p.open();
	        	p.createPendingGame(player.getLogin(), game.getGameId(),
	        			DateOperations.formatDate(game.getStartDate()),
	        			DateOperations.formatDate(game.getFinishDate()));
	        	p.close();
	        	data.putBoolean("resultOk", true);
	        	data.putSerializable("game", game);
				msg.setData(data);
				handler.sendMessage(msg);
			} catch (SQLiteException e) {
				data.putSerializable("SQLiteException", e);
	        	msg.setData(data);
				handler.sendMessage(msg);
			}
		}
	}
}
